/** * Dianping.com Inc. * Copyright (c) 2003-2013 All Rights Reserved. */ package com.dianping.pigeon.remoting.provider.config.spring; import java.util.concurrent.atomic.AtomicInteger; import com.dianping.pigeon.remoting.provider.config.PoolConfig; import com.dianping.pigeon.remoting.provider.config.PoolConfigFactory; import org.apache.commons.lang.StringUtils; import com.dianping.pigeon.log.Logger; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.dianping.pigeon.config.ConfigManager; import com.dianping.pigeon.config.ConfigManagerLoader; import com.dianping.pigeon.log.LoggerLoader; import com.dianping.pigeon.remoting.provider.config.ProviderMethodConfig; import com.dianping.pigeon.remoting.provider.process.threadpool.RequestThreadPoolProcessor; public class ServiceBeanDefinitionParser implements BeanDefinitionParser { /** Default placeholder prefix: "${" */ public static final String DEFAULT_PLACEHOLDER_PREFIX = "${"; /** Default placeholder suffix: "}" */ public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}"; private static final Logger logger = LoggerLoader.getLogger(ServiceBeanDefinitionParser.class); private final Class<?> beanClass; private final boolean required; private static AtomicInteger idCounter = new AtomicInteger(); private static ConfigManager configManager = ConfigManagerLoader.getConfigManager(); private static boolean checkRefExists = configManager.getBooleanValue("pigeon.config.spring.checkrefexists", false); public ServiceBeanDefinitionParser(Class<?> beanClass, boolean required) { this.beanClass = beanClass; this.required = required; } public BeanDefinition parse(Element element, ParserContext parserContext) { return parse(element, parserContext, beanClass, required); } private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) { RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setLazyInit(false); String id = element.getAttribute("id"); if (StringUtils.isBlank(id)) { id = "pigeonService_" + idCounter.incrementAndGet(); } beanDefinition.setBeanClass(SingleServiceBean.class); beanDefinition.setInitMethodName("init"); MutablePropertyValues properties = beanDefinition.getPropertyValues(); String ref = element.getAttribute("ref"); if (checkRefExists && !parserContext.getRegistry().containsBeanDefinition(ref)) { throw new IllegalStateException("service must have a reference to bean:" + ref); } properties.addPropertyValue("serviceImpl", new RuntimeBeanReference(ref)); if (element.hasAttribute("server")) { String server = element.getAttribute("server"); if (!parserContext.getRegistry().containsBeanDefinition(server)) { throw new IllegalStateException("service must have a reference to bean:" + server); } properties.addPropertyValue("serverBean", new RuntimeBeanReference(server)); } String url = null; if (element.hasAttribute("url")) { url = resolveReference(element, "url"); properties.addPropertyValue("url", url); } if (element.hasAttribute("interface")) { String interfaceName = resolveReference(element, "interface"); if (StringUtils.isBlank(url)) { url = interfaceName; } properties.addPropertyValue("interfaceName", interfaceName); } if (element.hasAttribute("version")) { properties.addPropertyValue("version", resolveReference(element, "version")); } if (element.hasAttribute("cancelTimeout")) { properties.addPropertyValue("cancelTimeout", resolveReference(element, "cancelTimeout")); } if (element.hasAttribute("useSharedPool")) { properties.addPropertyValue("useSharedPool", resolveReference(element, "useSharedPool")); } if (element.hasAttribute("pool")) { String pool = element.getAttribute("pool"); if (!parserContext.getRegistry().containsBeanDefinition(pool)) { throw new IllegalStateException("service must have a reference to bean:" + pool); } BeanDefinition poolBd = parserContext.getRegistry().getBeanDefinition(pool); String poolName = (String) poolBd.getPropertyValues().getPropertyValue("poolName").getValue(); int coreSize = (int) poolBd.getPropertyValues().getPropertyValue("corePoolSize").getValue(); int maxSize = (int) poolBd.getPropertyValues().getPropertyValue("maxPoolSize").getValue(); int queueSize = (int) poolBd.getPropertyValues().getPropertyValue("workQueueSize").getValue(); PoolConfig poolConfig = PoolConfigFactory.createPoolConfig(poolName, coreSize, maxSize, queueSize); properties.addPropertyValue("poolConfig", poolConfig); } else if (element.hasAttribute("actives")) { properties.addPropertyValue("actives", resolveReference(element, "actives")); } if (element.hasChildNodes()) { parseMethods(url, id, element.getChildNodes(), beanDefinition, parserContext); } parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); return beanDefinition; } private static BeanDefinition parseMethod(String url, String methodName, Element element, ParserContext parserContext, Class<?> beanClass, boolean required) { RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setLazyInit(false); String id = element.getAttribute("id"); if (StringUtils.isBlank(id)) { id = "pigeonService_" + idCounter.incrementAndGet(); } beanDefinition.setBeanClass(beanClass); MutablePropertyValues properties = beanDefinition.getPropertyValues(); if (element.hasAttribute("name")) { properties.addPropertyValue("name", resolveReference(element, "name")); } if (element.hasAttribute("pool")) { String pool = element.getAttribute("pool"); if (!parserContext.getRegistry().containsBeanDefinition(pool)) { throw new IllegalStateException("method must have a reference to bean:" + pool); } BeanDefinition poolBd = parserContext.getRegistry().getBeanDefinition(pool); String poolName = (String) poolBd.getPropertyValues().getPropertyValue("poolName").getValue(); int coreSize = (int) poolBd.getPropertyValues().getPropertyValue("corePoolSize").getValue(); int maxSize = (int) poolBd.getPropertyValues().getPropertyValue("maxPoolSize").getValue(); int queueSize = (int) poolBd.getPropertyValues().getPropertyValue("workQueueSize").getValue(); PoolConfig poolConfig = PoolConfigFactory.createPoolConfig(poolName, coreSize, maxSize, queueSize); properties.addPropertyValue("poolConfig", poolConfig); } else if (element.hasAttribute("actives")) { properties.addPropertyValue("actives", resolveReference(element, "actives")); String value = element.getAttribute("actives"); if (value.startsWith(DEFAULT_PLACEHOLDER_PREFIX) && value.endsWith(DEFAULT_PLACEHOLDER_SUFFIX)) { RequestThreadPoolProcessor.getMethodPoolConfigKeys().put(url + "#" + methodName, value.substring(2, value.length() - 1)); } } parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); return beanDefinition; } private static void parseMethods(String url, String id, NodeList nodeList, RootBeanDefinition beanDefinition, ParserContext parserContext) { if (nodeList != null && nodeList.getLength() > 0) { ManagedList methods = null; for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (node instanceof Element) { Element element = (Element) node; if ("method".equals(node.getNodeName()) || "method".equals(node.getLocalName())) { String methodName = element.getAttribute("name"); if (methodName == null || methodName.length() == 0) { throw new IllegalStateException("<pigeon:method> name attribute == null"); } if (methods == null) { methods = new ManagedList(); } BeanDefinition methodBeanDefinition = parseMethod(url, methodName, ((Element) node), parserContext, ProviderMethodConfig.class, false); String name = id + "." + methodName; BeanDefinitionHolder methodBeanDefinitionHolder = new BeanDefinitionHolder( methodBeanDefinition, name); methods.add(methodBeanDefinitionHolder); } } } if (methods != null) { beanDefinition.getPropertyValues().addPropertyValue("methods", methods); } } } private static String resolveReference(Element element, String attribute) { String value = element.getAttribute(attribute); if (value.startsWith(DEFAULT_PLACEHOLDER_PREFIX) && value.endsWith(DEFAULT_PLACEHOLDER_SUFFIX)) { String valueInCache = configManager.getStringValue(value.substring(2, value.length() - 1)); if (valueInCache == null) { throw new IllegalStateException("undefined config property:" + element.getAttribute(attribute)); } else { value = valueInCache; } } return value; } private static String resolveReference(BeanDefinition bd, String attribute) { String value = (String) bd.getAttribute(attribute); if (value.startsWith(DEFAULT_PLACEHOLDER_PREFIX) && value.endsWith(DEFAULT_PLACEHOLDER_SUFFIX)) { String valueInCache = configManager.getStringValue(value.substring(2, value.length() - 1)); if (valueInCache == null) { throw new IllegalStateException("undefined config property:" + bd.getAttribute(attribute)); } else { value = valueInCache; } } return value; } }